﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;

using Yawf.Core.Logs;

namespace Yawf.Core.Reflection {
	public class ReflectionUtils {
		private static Dictionary<String,String> simpleTypes = null;
		private static Dictionary<String,String> simpleTypeArrayCodes = null;
		private static Dictionary<String,String> simpleFullTypes = null;
		
		static ReflectionUtils(){
			simpleTypes = new Dictionary<String,String>();
			simpleTypes.Add("BOOL","System.Boolean");
			simpleTypes.Add("BYTE","System.Byte");
			simpleTypes.Add("CHAR","System.Char");
			simpleTypes.Add("DOUBLE","System.Double");
			simpleTypes.Add("FLOAT","System.Single");
			simpleTypes.Add("INT","System.Int32");
			simpleTypes.Add("LONG","System.Int64");
			simpleTypes.Add("SHORT","System.Int16");
			simpleTypes.Add("STRING","System.String");
			simpleTypes.Add("DECIMAL", "System.Decimal");
			
			simpleTypeArrayCodes = new Dictionary<String,String>();
			simpleTypeArrayCodes.Add("[Z","Z");
			simpleTypeArrayCodes.Add("[B","B");
			simpleTypeArrayCodes.Add("[C","C");
			simpleTypeArrayCodes.Add("[D","D");
			simpleTypeArrayCodes.Add("[F","F");
			simpleTypeArrayCodes.Add("[I","I");
			simpleTypeArrayCodes.Add("[J","L");
			simpleTypeArrayCodes.Add("[S","S");
			simpleTypeArrayCodes.Add("[LSystem.String;","System.String");

			simpleFullTypes = new Dictionary<String,String>();
			simpleFullTypes.Add("System.Boolean","BOOLEAN");
			simpleFullTypes.Add("System.Byte","BYTE");
			simpleFullTypes.Add("System.Char","CHAR");
			simpleFullTypes.Add("System.Double","DOUBLE");
			simpleFullTypes.Add("System.Single","FLOAT");
			simpleFullTypes.Add("System.Int32","INT");
			simpleFullTypes.Add("System.Int64","LONG");
			simpleFullTypes.Add("System.Int16","SHORT");
			simpleFullTypes.Add("System.String","STRING");
			simpleFullTypes.Add("System.Decimal", "DECIMAL");
			
		}

		/// <summary>
		/// Creates an instance of a Class.
		/// </summary>
		/// <param name="className">Full classifed Type Name, eg Namespace.Class, AssemblyName</param>
		/// <returns>Instance Object created</returns>
		public static Object CreateInstance(String className) {
			Type t;
			
			t = Type.GetType(className);
			return CreateInstance(t);
		}											// CreateInstance

		/// <summary>
		/// Creates an instance of a Class.
		/// </summary>
		/// <param name="type">Type of class to instanciate</param>
		/// <returns>Instance Object created</returns>
		public static Object CreateInstance(Type type) {
			return Activator.CreateInstance(type);
		}											// CreateInstance

		/// <summary>
		/// Translates typeName to a full Type name, eg if the TypeName is a
		/// Simple Type in short form, bool,int,long etc this method will translate
		/// to corresponding Type, eg System.Boolean,System.Int32 etc
		/// </summary>
		/// <param name="typeName">Name to translate</param>
		/// <returns></returns>
		public static string GetFullTypeName(String typeName) {
			if (simpleTypes.ContainsKey(typeName)) {
				return simpleTypes[typeName];
			}

			return typeName;
		}									// GetFullTypeName

		/// <summary>
		/// Checks if given type is a simple type. Simple types are
		/// bool,byte,double,float,int,long,short,String or
		/// its corresponding Object class 
		/// </summary>
		/// <param name="type">type to check</param>
		/// <returns>true if simpleType is a simple type, false other wise</returns>
		public static bool IsSimpleType(string type) {
			if (type != null) {
				return simpleTypes.ContainsKey(type.ToUpper()) || simpleFullTypes.ContainsKey(type);
			}
			return false;
		}

		/// <summary>
		/// Generates a simple type. This method is not case sensitive
		/// </summary>
		/// <param name="type">Class to generate, should be any of the simple 
		/// types, if not the type will be taken as System.String.
		/// Simple types are:
		/// bool - will return a System.Boolean object
		/// byte - will return a System.lang.Byte object
		/// char - will return a System.Char object
		/// double - will return a System.Double object
		/// float - will return a System.Single object
		/// int - will return a System.Int32 object
		/// long - will return a System.Int32 object
		/// short - will return a System.Int16 object
		/// String or any other value - will return a System.String object
		/// 
		/// </param>
		/// <param name="value">value to convert to simple type</param>
		/// <returns>Value as specified object</returns>
		public static Object GetSimpleType(String type,String value) {
			String tmpStr;

			if (IsSimpleType(type)) {
				if (type.ToLower() == "char" && value != null && value.Length > 0) {
					return value[0];
				} else {
					if (simpleTypes.ContainsKey(type.ToUpper())) {
						tmpStr = simpleTypes[type.ToUpper()];
						if (tmpStr != null && tmpStr != "") {
							return Convert.ChangeType(value,Type.GetType(tmpStr),NumberFormatInfo.InvariantInfo);
						}
					}

					// Check if its a full class name
					if (simpleFullTypes.ContainsKey(type)) {
						return Convert.ChangeType(value,Type.GetType(type),NumberFormatInfo.InvariantInfo);
					}
				}
			}

			return value;
		}													// GetSimpleType

		/// <summary>
		/// Gets the Type object of the property of object instance
		/// </summary>
		/// <param name="theObject">Instance of object</param>
		/// <param name="propName">Name of proprty to get Type of</param>
		/// <returns>Type object of propName, null if propertyy not found</returns>
		public static Type GetPropertyType(Object theObject,String propName) {
			PropertyInfo propInfo;
			Type propType = null;

			if (theObject != null && propName != null && propName.Trim() != "") {
				try {
					propInfo = theObject.GetType().GetProperty(propName);
					propType = propInfo.PropertyType;
				} catch (Exception e) {
					Log.ExceptionMessage(typeof(ReflectionUtils),"Property " + propName + " not found",e);
				}
			}

			return propType;
		}								// GetPropertyType

		/// <summary>
		/// Gets aproprty on an Object instance
		/// </summary>
		/// <param name="theObject">Object to set proprty on</param>
		/// <param name="propName">Name of property to get</param>
		public static Object GetProperty(Object theObject, String propName) {
			PropertyInfo propInfo;

			// Just return if in parameters are not valid
			if (theObject == null || propName == null || propName.Trim() == "") {
				return null;
			}

			try {
				propInfo = theObject.GetType().GetProperty(propName); ;
				return propInfo.GetValue(theObject, null);
			} catch (Exception e) {
				Log.ExceptionMessage(typeof(ReflectionUtils), "Error getting Property " + propName, e);
			}

			return null;
		}									// SetProperty

		/// <summary>
		/// Set s aproprty on an Object instance
		/// </summary>
		/// <param name="theObject">Object to set proprty on</param>
		/// <param name="propName">Name of property to set</param>
		/// <param name="value2Set">Value to set</param>
		public static void SetProperty(Object theObject,String propName,Object value2Set) {
			PropertyInfo propInfo;

			// Just return if in parameters are not valid
			if (theObject == null || propName == null || propName.Trim() == "") {
				return;
			}

			try {
				propInfo = theObject.GetType().GetProperty(propName); ;
				propInfo.SetValue(theObject,value2Set,null);
			} catch (Exception e) {
				Log.ExceptionMessage(typeof(ReflectionUtils), "Error setting Property " + propName + " to value " + value2Set, e);
			}
		}									// SetProperty

		/// <summary>
		/// Finds all methods with a certain methodName and a certain number of parameters
		/// </summary>
		/// <param name="theObject">Object to find methods on</param>
		/// <param name="methodName">Name of method to look for</param>
		/// <param name="noParams">Number of parameters that method should have</param>
		/// <returns>A list of MethodInfo objects</returns>
		public static IList<MethodInfo> FindMethodsByName(Object theObject,String methodName,int noParams) {
			List<MethodInfo> methods;
			MethodInfo[] tmpMethods;

			methods = new List<MethodInfo>();
			if (theObject != null && methodName != null && methodName.Trim() != "") {
				try {
					tmpMethods = theObject.GetType().GetMethods();
					for (int i = 0; tmpMethods != null && i < tmpMethods.Length; i++) {
						if (tmpMethods[i].Name == methodName) {
							methods.Add(tmpMethods[i]);
						}
					}
				} catch (Exception e) {
					Log.ExceptionMessage(typeof(ReflectionUtils), "Error Getting Method " + methodName + " on Object of Type " + theObject.GetType(), e);
				}
			}
			
			return methods;
		}										// FindMethodsByName

		/// <summary>
		/// Checks weather a Type can be constructed, eg its not an interface or an
		/// abstract class or a strcut nor a enum
		/// </summary>
		/// <param name="type">Type to check</param>
		/// <returns>Returns true if its possible to create new instance of the Type</returns>
		public static bool IsConstructable(Type type) {
			return type != null && !type.IsEnum && !type.IsInterface && !type.IsAbstract && type.IsClass;
		}												// IsConstructable

		/// <summary>
		/// Gets Type object, using a Type String, null if Type is not found 
		/// </summary>
		/// <param name="typeName">Name of type to look up</param>
		/// <returns>TYpe object, null if not found</returns>
		public static Type GetType(String typeName) {
			Type t = null;

			try {
				t = Type.GetType(typeName);
			} catch (Exception e) {
				Log.ExceptionMessage(typeof(ReflectionUtils), "Error getting Type object of " + typeName, e);
			}

			return t;
		}										// GetType
	}									// Class
}
